
#include "gtest/gtest.h"

#include <vector>
#include "fiber-env.h"
#include "native-mutex.h"

using namespace std;
using namespace dulymoo;

namespace test_sleep { namespace {

    struct context_t {
        vector<int> numbers;
        NativeMutex lock;

        void AddNumber(int n) {
            lock.Lock();
            numbers.push_back(n);
            lock.Unlock();
        }
    };

    void fiber1(void* arg)
    {
        context_t *context = reinterpret_cast<context_t*>(arg);

        FiberEnv::Sleep(5);
        context->AddNumber(2);
        FiberEnv::Sleep(10);
        context->AddNumber(4);
    }

    void fiber2(void* arg)
    {
        context_t *context = reinterpret_cast<context_t*>(arg);

        context->AddNumber(1);
        FiberEnv::Sleep(10);
        context->AddNumber(3);
        FiberEnv::Sleep(10);
        context->AddNumber(5);
    }

    TEST(FiberEnv, Sleep) {
        FiberEnv env;

        context_t context;

        EXPECT_TRUE(env.Initialize());
        EXPECT_TRUE(env.AddFiber(fiber1, (void*)(&context)));
        EXPECT_TRUE(env.AddFiber(fiber2, (void*)(&context)));
        EXPECT_TRUE(env.Start(1));
        env.Stop();
        env.Finalize();

        EXPECT_EQ(5, context.numbers.size());
        EXPECT_EQ(1, context.numbers[0]);
        EXPECT_EQ(2, context.numbers[1]);
        EXPECT_EQ(3, context.numbers[2]);
        EXPECT_EQ(4, context.numbers[3]);
        EXPECT_EQ(5, context.numbers[4]);
    }

}}  // namespace test_sleep::<anonymous>

namespace test_yield { namespace {

    struct context_t {
        vector<int> numbers;
        NativeMutex lock;

        void AddNumber(int n) {
            lock.Lock();
            numbers.push_back(n);
            lock.Unlock();
        }
    };

    void fiber1(void* arg)
    {
        context_t *context = reinterpret_cast<context_t*>(arg);

        FiberEnv::Yield();
        context->AddNumber(2);
        FiberEnv::Yield();
        context->AddNumber(4);
    }

    void fiber2(void* arg)
    {
        context_t *context = reinterpret_cast<context_t*>(arg);

        context->AddNumber(1);
        FiberEnv::Yield();
        context->AddNumber(3);
        FiberEnv::Yield();
        context->AddNumber(5);
    }

    TEST(FiberEnv, Yield) {
        FiberEnv env;

        context_t context;

        EXPECT_TRUE(env.Initialize());
        EXPECT_TRUE(env.AddFiber(fiber1, (void*)(&context)));
        EXPECT_TRUE(env.AddFiber(fiber2, (void*)(&context)));
        EXPECT_TRUE(env.Start(1));
        env.Stop();
        env.Finalize();

        EXPECT_EQ(5, context.numbers.size());
        EXPECT_EQ(1, context.numbers[0]);
        EXPECT_EQ(2, context.numbers[1]);
        EXPECT_EQ(3, context.numbers[2]);
        EXPECT_EQ(4, context.numbers[3]);
        EXPECT_EQ(5, context.numbers[4]);
    }

}}  // namespace test_yield::<anonymous>

namespace test_quit { namespace {

    struct context_t {
        vector<int> numbers;
        NativeMutex lock;

        void AddNumber(int n) {
            lock.Lock();
            numbers.push_back(n);
            lock.Unlock();
        }
    };

    void fiber1(void* arg)
    {
        context_t *context = reinterpret_cast<context_t*>(arg);

        context->AddNumber(1);
        FiberEnv::Quit();
        context->AddNumber(111);
    }

    void fiber2(void* arg)
    {
        context_t *context = reinterpret_cast<context_t*>(arg);

        context->AddNumber(2);
    }

    TEST(FiberEnv, Quit) {
        FiberEnv env;

        context_t context;

        EXPECT_TRUE(env.Initialize());
        EXPECT_TRUE(env.AddFiber(fiber1, (void*)(&context)));
        EXPECT_TRUE(env.AddFiber(fiber2, (void*)(&context)));
        EXPECT_TRUE(env.Start(1));
        env.Stop();
        env.Finalize();

        EXPECT_EQ(2, context.numbers.size());
        EXPECT_EQ(1, context.numbers[0]);
        EXPECT_EQ(2, context.numbers[1]);
    }

}}  // namespace test_quit::<anonymous>

